package org.moonlight.jvm.rtda.share.methodarea;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.moonlight.jvm.classfile.constant.ConstantInfo;
import org.moonlight.jvm.classfile.constant.StaticConstantPool;
import org.moonlight.jvm.classfile.constant.info.ConstantClassInfo;
import org.moonlight.jvm.classfile.constant.info.ConstantStringInfo;
import org.moonlight.jvm.classfile.constant.info.member.ConstantFieldRefInfo;
import org.moonlight.jvm.classfile.constant.info.member.ConstantInterfaceMethodRefInfo;
import org.moonlight.jvm.classfile.constant.info.member.ConstantMethodRefInfo;
import org.moonlight.jvm.classfile.constant.info.number.ConstantDoubleInfo;
import org.moonlight.jvm.classfile.constant.info.number.ConstantFloatInfo;
import org.moonlight.jvm.classfile.constant.info.number.ConstantIntegerInfo;
import org.moonlight.jvm.classfile.constant.info.number.ConstantLongInfo;
import org.moonlight.jvm.rtda.share.heap.Class;
import org.moonlight.jvm.rtda.share.heap.symref.ClassRef;
import org.moonlight.jvm.rtda.share.heap.symref.FieldRef;
import org.moonlight.jvm.rtda.share.heap.symref.InterfaceMethodRef;
import org.moonlight.jvm.rtda.share.heap.symref.MethodRef;

import java.io.Serializable;

/**
 * 运行时常量池
 *
 * 运行时常量池主要存放两类信息:
 *   1. 字面量（literal）
 *      字面量包括整数、浮点数和字符串字面量
 *   2. 符号引用（symbolic reference）
 *      符号引用包括类符号引用、字段符号引用、方法符号引用和接口方法符号引用
 *
 * @author Moonlight
 * @createTime 2023/8/29 16:17
 **/
@EqualsAndHashCode
public class RtConstantPool implements Serializable {

    /** 运行时常量池 所属的类 **/
    @Getter
    @Setter
    private Class clazz;
    /** 常量信息 **/
    private final Object[] constants;

    /**
     * 把从 ClassFile 中解析出来的 静态常量池 转换为 运行时常量池
     * @param clazz {@link Class}
     * @param scp 静态常量池
     * @createTime 17:44 2023/8/29
     * @author moonlight
     **/
    public RtConstantPool(Class clazz, StaticConstantPool scp) {
        int cpLen = scp.getConstantInfos().length;

        this.clazz = clazz;
        this.constants = new Object[cpLen];

        ConstantInfo[] constantInfos = scp.getConstantInfos();
        // 1. 表头给出的常量池大小比常量池的实际大小大 1，如表头的值为 n，实际大小就是 n-1。
        // 2. 有效的常量池索引为 1~n-1，0 是无效索引。
        // 3. CONSTANT_Long_info 和 CONSTANT_Double_info 占用常量池的两个位置。
        for (int i = 1; i < cpLen; i++) {
            ConstantInfo constantInfo = constantInfos[i];
            switch (constantInfo.tag()) {
                // 最简单的是int或float型常量，直接取出常量值，放进constants中即可；
                case ConstantInfo.CONSTANT_INTEGER:
                    ConstantIntegerInfo integerInfo = (ConstantIntegerInfo) constantInfo;
                    this.constants[i] = integerInfo.getVal();
                    break;
                case ConstantInfo.CONSTANT_FLOAT:
                    ConstantFloatInfo floatInfo = (ConstantFloatInfo) constantInfo;
                    this.constants[i] = floatInfo.getVal();
                    break;
                // Long 和 Double型常量，也是直接提取常量值放进constants中；
                // 但是这两种类型的常量在常量池中都是占据两个位置，所以索引要特殊处理
                // 不过我们这里是直接用 Object 的所以暂时先不考虑特殊处理索引了
                // 常量池(不管静态还是动态)都还是需要给两个位置的，因为编译出来的class文件就是给的两个位置
                // 虽然在 操作数栈 和 局部变量表 这些地方，本工程并没有给 Long 和 Double 两个位置
                case ConstantInfo.CONSTANT_LONG:
                    ConstantLongInfo longInfo = (ConstantLongInfo) constantInfo;
                    this.constants[i] = longInfo.getVal();
                    i++;
                    break;
                case ConstantInfo.CONSTANT_DOUBLE:
                    ConstantDoubleInfo doubleInfo = (ConstantDoubleInfo) constantInfo;
                    this.constants[i] = doubleInfo.getVal();
                    i++;
                    break;
                //如果是字符串常量，直接取出字符串，放进constants中
                case ConstantInfo.CONSTANT_STRING:
                    ConstantStringInfo stringInfo = (ConstantStringInfo) constantInfo;
                    this.constants[i] = stringInfo.str();
                    break;
                // 类的符号引用
                case ConstantInfo.CONSTANT_CLASS:
                    this.constants[i] = new ClassRef(this, (ConstantClassInfo) constantInfo);
                    break;
                // 字段的符号引用
                case ConstantInfo.CONSTANT_FIELDREF:
                    this.constants[i] = new FieldRef(this, (ConstantFieldRefInfo) constantInfo);
                    break;
                // 接口方法的符号引用
                case ConstantInfo.CONSTANT_INTERFACEMETHODREF:
                    this.constants[i] = new InterfaceMethodRef(this, (ConstantInterfaceMethodRefInfo) constantInfo);
                    break;
                // 方法的符号引用
                case ConstantInfo.CONSTANT_METHODREF:
                    this.constants[i] = new MethodRef(this, (ConstantMethodRefInfo) constantInfo);
                    break;
                default:
            }
        }
    }

    public Object getConstant(int idx) {
        return this.constants[idx];
    }

}
